Skip to content

docs: qwen4exp results, an upstream-sync warning, and a caveat on "bit-identical" - #10

Open
cpuchip wants to merge 2 commits into
thecodacus:perffrom
cpuchip:threadchip/moe-cache-qwen4exp-notes
Open

docs: qwen4exp results, an upstream-sync warning, and a caveat on "bit-identical"#10
cpuchip wants to merge 2 commits into
thecodacus:perffrom
cpuchip:threadchip/moe-cache-qwen4exp-notes

Conversation

@cpuchip

@cpuchip cpuchip commented Aug 27, 2026

Copy link
Copy Markdown

Ran your expert cache on Qwen3.8-Flash-Next (qwen4exp, 177B, 512 experts/layer, 10+1 active) on a single RTX 3090 — five cherry-picks onto ggml-org PR ggml-org#27742 and it worked. Three things a downstream reader of the guide would want, all docs-only.

1. A collision waiting on your next upstream sync. ggml-org ebb546b7e (ggml-org#26802, 2026-08-11) adds ggml_cuda_mul_mat_id_needs_sync() and asserts it at the single mul_mat_id caller. Your op_params[0] = 1 correctly disqualifies both fast paths, so the op reaches the sync path — but the new predicate predates the flag and answers "no sync needed" for a small quantized batch. First decode, every time:

ggml-cuda.cu:1948: GGML_ASSERT(ggml_cuda_mul_mat_id_needs_sync(dst, cc)) failed

Two lines fix it, on the branch linked in the README text. Not sent as a code PR because perf doesn't carry needs_sync yet — there's nothing for the patch to apply to. Ready whenever you take the sync.

2. A caveat on "bit-identical". On qwen4exp the cached output is not bit-identical — it diverges from baseline at temperature 0, coherent and on-topic, consistent with float non-associativity between one fused chain and two summed pack chains. Quality is unaffected as far as a proper test can tell: paired over the same 64 chunks, ΔPPL +0.047% (t = +0.88), below that test's own 0.107% detection floor, with the cached arm's peak VRAM confirming it engaged. Suggesting statistically indistinguishable rather than bit-identical for architectures beyond the three you measured — your claim may well hold exactly on those.

3. The silent no-op, added to your troubleshooting table: --moe-cache-slots without --moe-cache-profile does nothing, reports clean, and reads as a small regression in tok/s. VRAM is the only tell — it doesn't move a megabyte. Cost us an evening.

The numbers

arm VRAM decode
--n-cpu-moe 48, no cache 6,284 MiB 16.00 tok/s
--n-cpu-moe 48 + --moe-cache-slots 64 + profile 15,522 MiB 21.52 tok/s (+34.5%)
--n-cpu-moe 40 (whole layers on GPU instead) 18,784 MiB 17.63 tok/s (+10%)

The comparison we found most useful: moving whole layers onto the card spent 12.5 GB to buy 10%, the cache spent 9.2 GB to buy 34.5%. Oracle hit rates from llama-moe-trace + your simulate.py: 68.2% at 64 slots/layer, 88.0% at 128 (which OOMs on 24 GB).

We went in expecting the routing to be too flat for a cache to help — 512 fine-grained experts with load balancing look designed against one, and Laguna measured barely-skewed. It isn't: only 51.5% of (layer, expert) pairs are ever used.

Falsifier on all of the above: one card, one model, one quant (unsloth UD-Q4_K_XL), routing profile traced from a single prompt. Happy to adjust or drop any of it.

… caveat

Ran the expert cache on Qwen3.8-Flash-Next (qwen4exp, 177B, 512 experts/layer) on a
single RTX 3090: +34.5% decode for 9.2 GB, where moving whole layers with
--n-cpu-moe instead spent 12.5 GB to buy 10%.

Three things a downstream user would want in the guide:

1. After syncing past ggml-org ebb546b (ggml-org#26802), the pack chains trip
   GGML_ASSERT(ggml_cuda_mul_mat_id_needs_sync) on the first decode. op_params[0]=1
   correctly disqualifies both fast paths, but the new predicate predates the flag
   and answers 'no sync needed' for a small quantized batch. Two-line fix included;
   not a code PR because this branch has no needs_sync to patch.

2. On qwen4exp the output is NOT bit-identical to baseline -- it diverges at
   temperature 0, consistent with float non-associativity between one fused chain
   and two summed pack chains. Quality is unaffected as far as a proper test can
   see: paired over 64 chunks, dPPL +0.047% (t=+0.88) against that test's own
   0.107% detection floor.

3. --moe-cache-slots without --moe-cache-profile is SILENT. Added to the
   troubleshooting table: tok/s reads as a small regression and VRAM is the only
   tell.
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Aug 27, 2026
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 80dc3ce7-2c6f-42f5-8f5d-a63646fc4ae5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

The first draft cited only Pride and Prejudice, and that corpus is a poor
discriminator: baseline PPL 1.0594 means the model is near-certain of every token,
so there is little room for a defect to show. Re-ran the whole comparison on
technical prose (NASA SP-4205 OCR, baseline PPL 8.2438).

  NASA    cache-64 vs baseline  -0.047%  t -0.85  floor 0.112%
  Austen  cache-64 vs baseline  +0.113%  t +1.68  floor 0.107%

Neither detectable, and the SIGN FLIPS between corpora -- a real quality cost would
keep its sign. Both rows shown rather than only the favourable one.
@cpuchip

cpuchip commented Aug 27, 2026

Copy link
Copy Markdown
Author

Updated the quality evidence — the first version cited only Pride and Prejudice, and that corpus turned out to be a poor discriminator: baseline PPL 1.0594 means the model is near-certain of every token, which we read as memorisation rather than a usable test. Re-ran the whole comparison on technical prose the model has not swallowed (NASA SP-4205 OCR, baseline PPL 8.2438):

corpus baseline PPL cache-64 ΔPPL t detection floor
NASA SP-4205 OCR 8.2438 −0.047% −0.85 0.112%
Pride and Prejudice 1.0594 +0.113% +1.68 0.107%

64 paired chunks each, cached arm's peak VRAM 15,544 MiB against 6,286 for cache-off so it provably engaged. Neither delta is detectable — and the sign flips between corpora, which a real quality cost would not do. Both rows are in the diff rather than only the favourable one.

So the claim I would stand behind for qwen4exp: +34.5% decode for 9.2 GB, at no measurable quality cost, with the effect bounded under ~0.11% PPL. Still not bit-identical, which is the wording note in the PR.

One near-miss worth passing on, since anyone comparing perplexity runs can hit it: mid-run I read the cached arm at chunk 38 (cumulative 9.29) against the other arms' final values (8.24) and it looked 12.7% worse. Cumulative perplexity is not comparable across different chunk counts. At matched indices the cached arm was the lowest of the three at every checkpoint.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant